package lotterysystem.lotterysystem.service.Impl;

import jakarta.annotation.Resource;
import lotterysystem.lotterysystem.common.errorcode.ServiceErrorCodeConstants;
import lotterysystem.lotterysystem.common.utils.JacksonUtil;
import lotterysystem.lotterysystem.common.utils.RedisUtil;
import lotterysystem.lotterysystem.controller.param.DrawPrizeParam;
import lotterysystem.lotterysystem.controller.param.ShowWinningRecordsParam;
import lotterysystem.lotterysystem.dao.dataobject.*;
import lotterysystem.lotterysystem.dao.mapper.*;
import lotterysystem.lotterysystem.service.DrawPrizeService;
import lotterysystem.lotterysystem.service.dto.WinningRecordDTO;
import lotterysystem.lotterysystem.service.enums.ActivityPrizeStatusEnum;
import lotterysystem.lotterysystem.service.enums.ActivityPrizeTiersEnum;
import lotterysystem.lotterysystem.service.enums.ActivityStatusEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

import static lotterysystem.lotterysystem.common.config.DirectRabbitConfig.EXCHANGE_NAME;
import static lotterysystem.lotterysystem.common.config.DirectRabbitConfig.ROUTING;

@Service
public class DrawPrizeServiceImpl implements DrawPrizeService {

    private static final Logger logger = LoggerFactory.getLogger(DrawPrizeServiceImpl.class);

    private final String WINNING_RECORDS_PREFIX = "WINNING_RECORDS_";

    private final Long WINNING_RECORDS_TIMEOUT = 60 * 60 * 24 * 2L;
    @Resource
    private RabbitTemplate rabbitTemplate;

    @Resource
    private ActivityMapper activityMapper;

    @Resource
    private UserMapper userMapper;

    @Resource
    private PrizeMapper prizeMapper;

    @Resource
    private ActivityPrizeMapper activityPrizeMapper;

    @Resource
    private WinningRecordsMapper winningRecordsMapper;

    @Resource
    private RedisUtil redisUtil;


    @Override
    public void drawPrize(DrawPrizeParam param) {

        Map<String, String> map = new HashMap<>();
        map.put("messageId", String.valueOf(UUID.randomUUID()));
        map.put("messageData", JacksonUtil.writeValueAsString(param));
        // 发消息 : 交换机，绑定的key，消息体
        rabbitTemplate.convertAndSend(EXCHANGE_NAME, ROUTING, map);
        logger.info("mq消息发送成功：map={}", JacksonUtil.writeValueAsString(map));
    }

    @Override
    public Boolean checkDrawPrizeParam(DrawPrizeParam param) {

        ActivityDO activityDO = activityMapper.selectById(param.getActivityId());

        // 奖品是否存在，可从activity_prize中查，原因是保存activity做了本地事务，保证一致性
        ActivityPrizeDO activityPrizeDO = activityPrizeMapper.selectByAPId(
                param.getActivityId(), param.getPrizeId());
        // 活动或奖品是否存在
        if (null == activityDO || null == activityPrizeDO) {

//            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_OR_PRIZE_IS_EMPTY);
            logger.info("校验抽奖请求失败！失败原因：{}",ServiceErrorCodeConstants.ACTIVITY_OR_PRIZE_IS_EMPTY.getErrorMessage());
            return false;
        }
        // 活动是否有效
        if (activityDO.getStatus().equalsIgnoreCase(ActivityStatusEnum.COMPLETED.name())) {
//            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_COMPLETED);
            logger.info("校验抽奖请求失败！失败原因：{}",ServiceErrorCodeConstants.ACTIVITY_COMPLETED.getErrorMessage());
            return false;
        }
        // 奖品是否有效
        if (activityDO.getStatus().equalsIgnoreCase(ActivityPrizeStatusEnum.COMPLETED.name())) {
//            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_COMPLETED);
            logger.info("校验抽奖请求失败！失败原因：{}",ServiceErrorCodeConstants.ACTIVITY_PRIZE_COMPLETED.getErrorMessage());
            return false;
        }
        // 中奖者人数是否和设置奖品数量一致
        if (activityPrizeDO.getPrizeAmount() != param.getWinnerList().size()) {
//            throw new ServiceException(ServiceErrorCodeConstants.WINNER_PRIZE_AMOUNT_ERROR);
            logger.info("校验抽奖请求失败！失败原因：{}",ServiceErrorCodeConstants.WINNER_PRIZE_AMOUNT_ERROR.getErrorMessage());
            return false;
        }
        return true;
    }

    @Override
    public List<WinnerRecordsDO> saveWinnerRecords(DrawPrizeParam param) {
        // 查询相关信息：活动，人员，奖品,活动关联奖品表
        ActivityDO activityDO = activityMapper.selectById(param.getActivityId());
        List<UserDO> userDOList = userMapper.batchSelectByIds(param.getWinnerList()
                .stream()
                .map(DrawPrizeParam.Winner::getUserId)
                .collect(Collectors.toList())
        );
        PrizeDO prizeDO = prizeMapper.selectById(param.getPrizeId());
        ActivityPrizeDO activityPrizeDO = activityPrizeMapper.selectByAPId(param.getActivityId(), param.getPrizeId());
        // 构造记录，保存记录

        List<WinnerRecordsDO> winnerRecordsDOList = userDOList.stream()
                .map(userDO -> {
                    WinnerRecordsDO winnerRecordsDO = new WinnerRecordsDO();
                    winnerRecordsDO.setActivityId(activityDO.getId());
                    winnerRecordsDO.setActivityName(activityDO.getActivityName());
                    winnerRecordsDO.setPrizeId(prizeDO.getId());
                    winnerRecordsDO.setPrizeName(prizeDO.getName());
                    winnerRecordsDO.setPrizeTier(activityPrizeDO.getPrizeTiers());
                    winnerRecordsDO.setWinnerId(userDO.getId());
                    winnerRecordsDO.setWinnerName(userDO.getUserName());
                    winnerRecordsDO.setWinnerEmail(userDO.getEmail());
                    winnerRecordsDO.setWinnerPhoneNumber(userDO.getPhoneNumber());
                    winnerRecordsDO.setWinningTime(param.getWinningTime());
                    return winnerRecordsDO;
                }).collect(Collectors.toList());
        winningRecordsMapper.batchInsert(winnerRecordsDOList);
        // 缓存记录
        // 1.缓存奖品维度的中奖记录(WinnerRecords_activityId+prizeId,winnerRecordsDOList(奖品维度的中奖名单))
        cacheWinningRecords(param.getActivityId() + "_" + param.getPrizeId(),
                winnerRecordsDOList,
                WINNING_RECORDS_TIMEOUT);
        // 2.缓存活动维度的中奖记录(WinnerRecords_activityId,winnerRecordsDOList(活动维度的中奖名单))
        // 当活动已完成再去存放活动维度中奖记录
        if (activityDO.getStatus()
                .equalsIgnoreCase(ActivityStatusEnum.COMPLETED.name())) {
            // 查询活动维度的全量中奖记录
            List<WinnerRecordsDO> allList = winningRecordsMapper.selectByActivityId(param.getActivityId());
            cacheWinningRecords(String.valueOf(param.getActivityId()),
                    allList,
                    WINNING_RECORDS_TIMEOUT);
        }
        return winnerRecordsDOList;
    }

    @Override
    public void deleteRecords(Long activityId, Long prizeId) {
        if (null == activityId) {
            logger.warn("要删除的中奖记录相关的活动id为空！");
            return;
        }
        // 删除数据表
        winningRecordsMapper.deleteRecords(activityId,prizeId);
        // 删除缓存（奖品缓存，活动缓存）
        if (null != prizeId) {
            deleteWinningRecords(activityId+ "_" + prizeId);
        }

        // 无论是否传prizeId，都需要删除活动的中奖记录
        // 如果传prizeId，证明奖品未抽奖，必须删除活动下的缓存记录
        // 没传prizeId，只是删除活动的缓存信息
        deleteWinningRecords(String.valueOf(activityId));
    }

    @Override
    public List<WinningRecordDTO> getRecords(ShowWinningRecordsParam param) {
        // 查询redis
        String key = null == param.getPrizeId()
                ? String.valueOf(param.getActivityId())
                : param.getActivityId() + "_" + param.getPrizeId();
        List<WinnerRecordsDO> winnerRecordsDOList = getWinnerRecords(key);
        if (!CollectionUtils.isEmpty(winnerRecordsDOList)) {
            return convertToWinningRecordDTOList(winnerRecordsDOList);
        }
        // 查库
        winnerRecordsDOList = winningRecordsMapper.selectByActivityIdOrPrizeId(param.getActivityId(), param.getPrizeId());
        // 存放记录到redis
        if (CollectionUtils.isEmpty(winnerRecordsDOList)) {
            logger.info("查询的中奖记录为空！param：{}",JacksonUtil.writeValueAsString(param));
            return Arrays.asList();
        }
        cacheWinningRecords(key,winnerRecordsDOList,WINNING_RECORDS_TIMEOUT);
        return convertToWinningRecordDTOList(winnerRecordsDOList);
    }

    private List<WinningRecordDTO> convertToWinningRecordDTOList(List<WinnerRecordsDO> winnerRecordsDOList) {
        if (CollectionUtils.isEmpty(winnerRecordsDOList)) {
            return Arrays.asList();
        }
        return winnerRecordsDOList.stream()
                .map(winnerRecordsDO -> {
                    WinningRecordDTO winningRecordDTO = new WinningRecordDTO();
                    winningRecordDTO.setWinnerId(winnerRecordsDO.getWinnerId());
                    winningRecordDTO.setWinnerName(winnerRecordsDO.getWinnerName());
                    winningRecordDTO.setPrizeName(winnerRecordsDO.getPrizeName());
                    winningRecordDTO.setPrizeTier(ActivityPrizeTiersEnum.forName(winnerRecordsDO.getPrizeTier()));
                    winningRecordDTO.setWinningTime(winnerRecordsDO.getWinningTime());
                    return winningRecordDTO;
                }).collect(Collectors.toList());
    }


    /**
     * 缓存中奖记录
     *
     * @param key
     * @param winnerRecordsDOList
     * @param time
     */
    private void cacheWinningRecords(String key,
                                     List<WinnerRecordsDO> winnerRecordsDOList,
                                     Long time) {

        String str = "";
        try {

            if (!StringUtils.hasText(key)
                    || CollectionUtils.isEmpty(winnerRecordsDOList)) {
                logger.warn("要缓存的内容为空！key:{},value:{}",
                        key, JacksonUtil.writeValueAsString(winnerRecordsDOList));
                return;
            }
            str = JacksonUtil.writeValueAsString(winnerRecordsDOList);
            redisUtil.set(WINNING_RECORDS_PREFIX + key,
                    str,
                    time);
        } catch (Exception e) {
            logger.error("缓存中奖记录异常！key:{},value:{}", WINNING_RECORDS_PREFIX + key, str);
        }

    }

    /**
     * 从缓存中获取中奖记录
     * @param key
     * @return
     */
    private List<WinnerRecordsDO> getWinnerRecords(String key) {
        try {
            if (!StringUtils.hasText(key)) {
                logger.warn("要从缓存中查询中奖记录的key为空！");
                return Arrays.asList();
            }
            String str = redisUtil.get(WINNING_RECORDS_PREFIX + key);
            if (!StringUtils.hasText(str)) {
                return Arrays.asList();
            }
            return JacksonUtil.readListValue(str, WinnerRecordsDO.class);
        } catch (Exception e) {
            logger.error("从缓存中获取中奖记录异常！key:{}", WINNING_RECORDS_PREFIX + key);
            return Arrays.asList();
        }
    }
    private void deleteWinningRecords(String key) {
        try {
            if (redisUtil.hasKey(WINNING_RECORDS_PREFIX + key)) {
                // 存在再删除
                redisUtil.del(WINNING_RECORDS_PREFIX + key);
            }
        } catch (Exception e) {
            logger.error("删除中奖记录缓存异常，key：{}",key);
        }
    }
}
